#ifndef __SYNC_QUEUE_HPP__
#define __SYNC_QUEUE_HPP__

#include <memory>
#include <mutex>
#include <deque>
#include <queue>
#include <condition_variable>

template <typename T>
class SyncQueue
{
public:
    SyncQueue() = default;
    SyncQueue(const SyncQueue &) = delete;
    SyncQueue(const SyncQueue &&) = delete;
    ~SyncQueue() = default;

    bool empty() const
    {
        std::lock_guard<std::mutex> lk(_mutex);
        return _queue.empty();
    }

    void push(const T &data)
    {
        std::lock_guard<std::mutex> lk(_mutex);
        _queue.push(data);
        _cond.notify_one();
    }
    void push(T &&data)
    {
        std::lock_guard<std::mutex> lk(_mutex);
        _queue.push(std::move(data));
        _cond.notify_one();
    }

    std::shared_ptr<T> pop()
    {
        std::unique_lock<std::mutex> lk(_mutex);
        _cond.wait(lk, [this]()
                   { return !this->_queue.empty(); });
        auto ret = std::make_shared<T>(_queue.front());
        _queue.pop();
        return ret;
    }
    std::shared_ptr<T> try_pop()
    {
        std::unique_lock<std::mutex> lk(_mutex);
        if (_queue.empty())
            return {};
        auto ret = std::make_shared<T>(_queue.front());
        _queue.pop();
        return ret;
    }

private:
    std::queue<T> _queue;
    mutable std::mutex _mutex;
    std::condition_variable _cond;
};

#endif
